package xaj.meta.visit;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.File;
import java.util.*;

import polyglot.ast.Block;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.frontend.Job;
import polyglot.lex.EOF;
import polyglot.lex.Token;
import polyglot.types.TypeSystem;
import polyglot.util.CodeWriter;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import xaj.scc.ast.NamedFactor;
import xaj.scc.ast.NumberOfPasses;
import xaj.scc.ast.Production;
import xaj.scc.ast.SemanticAction;
import xaj.scc.ast.SimpleFactor;
import xaj.scc.ast.StringFactor;
import xaj.scc.ast.SyntaxClassBody;
import xaj.scc.ast.SyntaxClassDecl;
import xaj.scc.ast.SyntaxDeclarationInsertion;
import xaj.scc.ast.ProductionElement;
import xaj.meta.util.mapping.Mapping;


public class Visitor_Giorgio_01 extends XAJVisitor {
	
	private static PrintStream output;
	private static String sourceNonTerminals;
	private static String sourceTerminals;
	private static String sourceNewTokens;
	private static String source;
	private String fileName;
	private String className;
	private String newToken;
	
	private static PrintStream outputFlex;
	
	private int contProd;
	
	private static Queue<String> terminais = new LinkedList<String>();
	
	/*public NodeVisitor begin() {
        init();
		return this;
    }
	
	public void finish() {
		end();
	}*/
	
	public static void init() {
		try {
			output = new PrintStream("tmp/xaj.ppg");
			outputFlex = new PrintStream("tmp/syntaxclass12.flex");
			
			//TODO criar arquivos do Giorgio aqui tambem no projeto do Felipe
			
			Scanner input = new Scanner( new File("src/xaj/meta/parse/syntaxclass1.flex") );
			
			while( input.hasNext() ) {
				outputFlex.append(input.nextLine() + "\n");
			}
			
			outputFlex.append("\n\n");
			input.close();
			
			System.out.println("ABRIU XAJ.PPG");
			System.out.println("ABRIU SYNTAXCLASS12.FLEX");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		sourceNonTerminals = "\n\n//--------------------Non Terminals-------------------\n\n";
	}
	
	public static void end() {
		
		try {
			Scanner input = new Scanner ( new File( "src/xaj/meta/parse/syntaxclass2.flex" ) );
			outputFlex.append("\n\n");
			while( input.hasNext() )
				outputFlex.append(input.nextLine() + "\n");
		
			input.close();
			outputFlex.close();
		}
		catch(FileNotFoundException e){
			e.printStackTrace();
		}
		
		if( !terminais.isEmpty() ) {
			sourceTerminals = "\n\n//--------------------new Terminals-------------------\n\n";
			sourceTerminals += "terminal Token " + terminais.remove();
			while( !terminais.isEmpty() )
				sourceTerminals += ", " + terminais.remove();
		}
		
		sourceTerminals += "\n\n";
		sourceNonTerminals += "\n\n";
		
		output.append(sourceTerminals);
		output.append(sourceNonTerminals);
		output.append(source);
		output.close();
		System.out.println("FECHOU XAJ.PPG");
	}
	
	
	public Visitor_Giorgio_01(Job job, TypeSystem typeSystem, NodeFactory nodeFactory) {
		super(job, typeSystem, nodeFactory);	
	}

	public void visitNumberOfPasses(NumberOfPasses np) {
		/*
		if (getVisitType() == VisitType.ENTER) {
			System.out.println("-------------- Entrou no NumberOfPasses");
			System.out.println(np.getNumber());
		} else if (getVisitType() == VisitType.LEAVE) {
			System.out.println("-------------- Saiu do NumberOfPasses");
			System.out.println(np.getNumber());
		}
		*/
	}

	public void visitSyntaxClassDecl(SyntaxClassDecl scd) {
		if (getVisitType() == VisitType.ENTER) {
			className = scd.name();
			source += "\n\n//--------------- syntaxclass " + className + "\n";
		}
		/*
		if (getVisitType() == VisitType.ENTER) {
			try {
				Visitor_Giorgio_01 novo = new Visitor_Giorgio_01(job(), typeSystem(), nodeFactory());
				novo.fileName = "tmp/" + scd.name() + ".ppg";
				System.out.println("Abre arquivo: " + novo.fileName);
				novo.output = new PrintStream(novo.fileName);
				novo.output.append("import syntax." + scd.name() + ";\n\n");
				//novo.output.append("non terminal" + mapeamento.symbol.get(scd.) + scd.name() + ";\n\n");
				novo.className = scd.name();
				changeVisitor(novo);
			} catch(FileNotFoundException e) {
				System.err.println("error file");
			}
		} else if (getVisitType() == VisitType.LEAVE) {
			output.append(";");
			System.out.println("Fecha arquivo: " + fileName);
			output.close();
		}
		*/
	}
	
	public void visitSyntaxDeclarationInsertion(SyntaxDeclarationInsertion sdi) {
		if (getVisitType() == VisitType.ENTER) {
			Mapping mapeamento = Mapping.getInstance();
			if( sdi.getNonTerminalExtended() != null )
				source += "extend " + sdi.getNonTerminalExtended() + " ::= " + className + ":rt {: RESULT = rt; :};\n\n";
			//source += className + " ::= ";
		} else if (getVisitType() == VisitType.LEAVE) {
			//output.append( "{:\n\n" );
		}		
	}
	
	public void visitSimpleFactor(SimpleFactor sf) {
		if (getVisitType() == VisitType.ENTER) {
			source += " " + sf.getNonTerminal() + " ";
		} else if (getVisitType() == VisitType.LEAVE) {
		}		
	}
	
	public void visitNamedFactor(NamedFactor nf) {
		if (getVisitType() == VisitType.ENTER) {
			//output.append(":");
		} else if (getVisitType() == VisitType.LEAVE) {
			source += ":" + nf.getName() + " ";
		}
	}

	public void visitStringFactor(StringFactor sf){
		if (getVisitType() == VisitType.ENTER) {
			int codigo = 0;
			Mapping mapeamento = Mapping.getInstance();
			StringReader reader = new StringReader(sf.getString());
			xaj.meta.parse.Lexer_c lexer = 
				new xaj.meta.parse.Lexer_c(reader, null, null);
			try {
				Queue<Token> queue = new LinkedList<Token>();
				while (true) {
					
					Token t = lexer.nextToken();
					if ( t instanceof EOF ) {
						break;
					}
					queue.add(t);
				}
				
				if( !queue.isEmpty() ) {
					if( queue.size() > 1 ) {
						
						String operator = queue.peek().symbol().value.toString().replace("operator ", "");
						newToken = mapeamento.symbol.get(queue.remove().symbol().sym);
						
						while( !queue.isEmpty() ) {
							operator += queue.peek().symbol().value.toString().replace("operator ", "");
							newToken += '_' + mapeamento.symbol.get(queue.remove().symbol().sym);
						}
						
						source += newToken;
						
						if( !terminais.contains(newToken) ) {
							terminais.add(newToken);
						    outputFlex.append("\t" + "\"" + operator + "\"" + "\t" + "{ return op(sym." + newToken + ");\t }\n");
						}
					}
					else {
						//newToken = mapeamento.symbol.get(queue.remove().symbol().sym);
					
					
					
					/*
					System.out.println(newToken);
					source += newToken;
					newToken = "";
					*/
					
					codigo = queue.remove().symbol().sym;
					if (!mapeamento.symbol.containsKey(codigo)) {
						// AQUI TERIA QUE ENVIAR MSG DE ERRO!
					} else {
						if( mapeamento.symbol.get(codigo).equals("LPAREN") )
						    source += mapeamento.symbol.get(codigo) + " \n\t\t\t\t ";
						else
							source += mapeamento.symbol.get(codigo) + " ";
					}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void visitProduction(Production p){
		if(getVisitType() == VisitType.ENTER){
			contProd = p.getRightSide().size();
			source += p.getLeftSide() + " ::= ";
			sourceNonTerminals += "non terminal " + p.getLeftSide() + ";\n";
		}
		else if(getVisitType() == VisitType.LEAVE){
			source += ";\n";
		}
	}
	
	public void visitProductionElement(ProductionElement pe){
		if(getVisitType() == VisitType.LEAVE){
			--contProd;
			if (contProd > 0) {
				source += "|\n";
			}
			
		}
		
	}
		
	public void visitSemanticAction(SemanticAction sa) {
		if (getVisitType() == VisitType.ENTER) {
			Block b = sa.getBlock();
			if (b != null) {
				source += "\n\t\t\t\t {:\n";
				ByteArrayOutputStream ba = new ByteArrayOutputStream();
				CodeWriter cd = new CodeWriter(ba,10);
				PrettyPrinter pp = new PrettyPrinter();
				Iterator it = sa.getBlock().statements().iterator();
				while (it.hasNext()) {
					Stmt x = (Stmt) it.next();
					pp.printAst(x, cd);
					cd.newline();
//					source += "\n";
				}
				source += ba.toString();
				source += "\t\t\t\t :}";
			}
		}
		else if (getVisitType() == VisitType.LEAVE) {
		}
	}

	

}
